// ::1 Pick

interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

type MyPick<T extends object, K extends keyof T> = {
  [P in K]: T[P];
};

type TodoPreview = MyPick<Todo, "title" | "completed">;

const todo: TodoPreview = {
  title: "Clean room",
  completed: false,
};

// ::2 Readonly
interface Todo2 {
  title: string;
  description: string;
  fav: Record<string, string | number>;
}

type MyReadonly<T extends object> = {
  readonly [K in keyof T]: T[K] extends object ? MyReadonly<T[K]> : T[K];
};

const todo2: MyReadonly<Todo2> = {
  title: "Hey",
  description: "foobar",
  fav: {
    name: "sleep",
  },
};

// todo2.title = "Hello" // Error: cannot reassign a readonly property
// todo2.description = "barFoo" // Error: cannot reassign a readonly property
// todo2.fav.name = 'sports'

// ::3 TupleToObject TupleToUnion UnionToObject

// 元组转换为联合类型，非常简单 tuple[number]
type TupleToUnion<T extends ReadonlyArray<unknown>> = T[number];

// 联合类型转换为对象
type UnionToObject<U extends PropertyKey> = {
  [P in U]: P
}

type Result3_2 = UnionToObject<'hello' | 'world' | 'again'>

type Tuple = ["tesla", "model 3", "model X", "model Y"]
type Values = TupleToUnion<Tuple>;
type TupleToObject<T extends ReadonlyArray<string>> = {
  [K in TupleToUnion<T>]: K;
};
type result3 = TupleToObject<Tuple>; // expected { tesla: 'tesla', 'model 3': 'model 3', 'model X': 'model X', 'model Y': 'model Y'}

type TupleToObject2<T extends ReadonlyArray<string>> = UnionToObject<TupleToUnion<T>>
type Result3_3 = TupleToObject2<Tuple>



// ::4 First Last

type arr1 = ["a", "b", "c"];
type arr2 = [3, 2, 1];

type arr = [];
// type Aoo<T> = T extends [infer F, ...infer M] ? F : M  // why can not use M?
type Aoo4<T> = T extends [infer F, ...infer M] ? F : 1;
type Result4 = Aoo4<arr>;

type First<T extends any[]> = T extends [infer F, ...infer K] ? F : never;
type Last<T extends any[]> = T extends [...infer F, infer L] ? L : never;
type head1 = First<arr1>; // expected to be 'a'
type head2 = First<arr2>; // expected to be 3

// ::5 Length

type tesla = ["tesla", "model 3", "model X", "model Y"];
type spaceX = [
  "FALCON 9",
  "FALCON HEAVY",
  "DRAGON",
  "STARSHIP",
  "HUMAN SPACEFLIGHT"
];
type Length<T extends readonly any[]> = T["length"];

type teslaLength = Length<tesla>; // expected 4
type spaceXLength = Length<spaceX>; // expected 5

// ::6 MyExclude MyExtract

type MyExclude<T, E> = T extends E ? never : T;
type Result6 = MyExclude<"a" | "b" | "c", "a">; // 'b' | 'c'

type MyExtract<T, I> = I extends T ? I : never;
type Result6_2 = MyExtract<"a" | "b" | "c", "a" | "d">;

// ::7 MyAwaited

type ExampleType = Promise<Promise<string>>;
type ExampleType2 = Promise<number>;
type MyAwaited<T> = T extends PromiseLike<infer R> ? MyAwaited<R> : T;
type Result7 = MyAwaited<ExampleType>; // string
type Result7_2 = MyAwaited<ExampleType2>; // number

// ::8 If
type If<C extends boolean, T, F> = C extends true ? T : F;
type A = If<true, "a", "b">; // expected to be 'a'
type B = If<false, "a", "b">; // expected to be 'b'

// ::9 Concat
type Concat<T extends any[], U extends any[]> = [...T, ...U];
type Result9 = Concat<[1], [2]>; // expected to be [1, 2]

// ::10 Includes
type Includes<T extends any[], U> = U extends T[number] ? true : false;
type isPillarMen = Includes<["Kars", "Esidisi", "Wamuu", "Santana"], "Dio">; // expected to be `false`
type isPillarMen2 = Includes<["Kars", "Esidisi", "Wamuu", "Santana"], "Kars">; // expected to be `true`

// ::11 Push Unshift
type Push<T extends any[], U> = [...T, U];
type Unshift<T extends any[], U> = [U, ...T];
type Result11 = Push<[1, 2], "3">; // [1, 2, '3']
type Result11_2 = Unshift<[1, 2], "3">; // ['3', 1, 2]

// ::12 MyParameters
const foo = (arg1: string, arg2: number): void => {};
type MyParameters<T extends (...args: any[]) => any> = T extends (
  ...args: infer A
) => any
  ? A
  : never;
type FunctionParamsType = MyParameters<typeof foo>; // [arg1: string, arg2: number]

// ::13 MyReturnType
const fn = (v: boolean) => {
  if (v) return 1;
  else return 2;
};
type MyReturnType<T extends (...args: any[]) => any> = T extends (
  ...args: any[]
) => infer R
  ? R
  : never;
type Result13 = MyReturnType<typeof fn>; // 应推导出 "1 | 2"

// ::14 MyOmit
interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

type MyOmit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
type TodoPreview14 = MyOmit<Todo, "description" | "title">;

const todo14: TodoPreview14 = {
  completed: false,
};

// ::15 Chainable
declare const config: Chainable;

interface Chainable<T = {}> {
  get(): T;
  option<K extends string, V>(key: K, value: V): Chainable<T & { [P in K]: V }>;
}

const result = config
  .option("foo", 123)
  .option("name", "type-challenges")
  .option("bar", { value: "Hello World" })
  .get();

// 期望 result 的类型是：
// interface Result {
//   foo: number
//   name: string
//   bar: {
//     value: string
//   }
// }

// ::16 Promise.all MapTuple
const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise<string>((resolve, reject) => {
  setTimeout(resolve, 100, "foo");
});

type tuple = ["how", "are", "you"]; // 数组是特殊的对象 {0: 'how', 1: 'are', 2: 'you'}
type MapTuple<T extends ReadonlyArray<any>> = {
  [K in keyof T]: (...args: any[]) => T[K];
};
type aoo = MapTuple<tuple>;

// declare function PromiseAll<T extends readonly any[]>(plist: T): null
declare function PromiseAll<T extends ReadonlyArray<any>>(
  plist: T
): Promise<{
  [K in keyof T]: Awaited<T[K]>; // type of plist[K] is awaited<T[K]>
}>;

// 应推导出 `Promise<[number, 42, string]>`
const p = PromiseAll([promise1, promise2, promise3] as const);

// :17 PartReadonly

interface Todo17 {
  title: string;
  description: string;
  completed: boolean;
}

type Keys17 = "a" | "b" | "c";
type WantKeys17 = "c" | "d";
type Result17 = Extract<Keys17, WantKeys17>;

type PartReadonly<T extends object, U extends keyof T = keyof T> = 
    Readonly<Pick<T, U>> & Pick<T, Exclude<keyof T, U>>

type PartReadonly2<T extends object, U = never> = [U] extends [never]
  ? {
      readonly [P in keyof T]: T[P];
    }
  : {
      readonly [P in Extract<keyof T, U>]: T[P];
    } & {
      [P in Exclude<keyof T, U>]: T[P];
    };

const todo17: PartReadonly<Todo17, "title" | "description"> = {
  title: "Hey",
  description: "foobar",
  completed: false,
};

// todo17.title = "Hello"; // Error: cannot reassign a readonly property
// todo17.description = "barFoo"; // Error: cannot reassign a readonly property
// todo17.completed = true; // OK

type todo17_2 = PartReadonly<Todo17>;

// :18 LookUp
interface Cat {
  type: "cat";
  breeds: "Abyssinian" | "Shorthair" | "Curl" | "Bengal";
}

interface Dog {
  type: "dog";
  breeds: "Hound" | "Brittany" | "Bulldog" | "Boxer";
  color: "brown" | "white" | "black";
}

//   利用泛型变量，组装新的类型 用于条件类型的判断 nice
type LookUp<U, T> = U extends { type: T } ? U : never;
type MyDog = LookUp<Cat | Dog, "dog">; // expected to be `Dog`


// :19 TrimLeft TrimRight Trim
// 字符串的推导需要用模板字符串，在js表达式环境推导
type TrimLeft<T extends string> = T extends `${ ' ' | '\t' | '\n'}${infer R}` ? TrimLeft<R> : T
type TrimRight<T extends string> = T extends `${infer R}${ ' ' | '\t' | '\n'}` ? TrimRight<R> : T
type Trim<T extends string> = TrimRight<TrimLeft<T>>
type trimed = TrimLeft<'  Hello World  '> // 应推导出 'Hello World  '
type trimed2 = TrimRight<'  Hello World  '>
type trimed3 = Trim<'  Hello World  '> // expected to be 'Hello World'

// :20 MyCapitalize
// 需用到内置类型工具 Uppercase
type MyCapitalize<T extends string> = T extends `${infer F}${infer R}` ? `${ Uppercase<F> }${R}` : T
type capitalized = MyCapitalize<'hello world'> // expected to be 'Hello world'


// :21 Replace ReplaceAll
type Replace<S extends string, F extends string, T extends string> = S extends `${infer A}${F}${infer L}` ? `${A}${T}${L}` : S
type replaced = Replace<'types are fun!', 'fun', 'awesome'> // 期望是 'types are awesome!'

type str21 = 'hello world'
type Replace2<S extends string, F extends string, T extends string> = S extends `${infer A}${F}${infer L}` ? `${A}${T}${L}` : S
type result21 = Replace2<str21, 'y', 'mm'>

type ReplaceAll<S extends string, F extends string, T extends string> = S extends `${infer A}${F}${infer L}` ? ReplaceAll<`${A}${T}${L}`, F, T> : S
type result21_2 = ReplaceAll<str21, 'l', 'L'>

// 22: Permutation

type PK = PropertyKey


// U extends any 用这种方式来遍历联合类型
type Union22 = 'a' | 'b' | 'c'

// 23: UnionToTuple MinusOne

// 函数重载推导出来的函数签名为最后一个声明
declare function Fn(p: 1 | 2): 'a'
declare function Fn(p: 2 | 3): 'b'
type Result23 = ReturnType<typeof Fn> // 'b'
type Result23_2 = Parameters<typeof Fn> // p: 2 | 3

// 函数类型的交叉类型有类似函数重载的效果
type Fn23 = ((p: 1 | 2) => 'a') & ((p: 2 | 3) => 'b')
type Result23_3 = ReturnType<Fn23>
type Result23_4 = Parameters<Fn23>

type Fun23 = ((p: 1 | 2) => 'a') | ((p: 2 | 3) => 'b')
type R23 = ReturnType<Fun23>
type P23 = Parameters<Fun23>
// 函数的联合类型推导为新的函数签名，函数参数签名推导为各个子函数参数类型的交叉类型 
type P23_2 = Fun23 extends (p: infer P) => 'a' | 'b' ? P : 0


// 联合类型 映射为 返回对应元素值的函数的联合类型
type ToUFn<U> = U extends any ? () => U : never

// 两个函数类型 的合并，得到最后一个函数类型
type CombindFn = (() => 'a') & (() => 'b')
type CombinReturnType = ReturnType<CombindFn>
type CombinParamType = Parameters<((f: 'one' | 'two') => 'a') & ((f: 'three' | 'four') => 'a')>;

type U2I<U> = (U extends any ? (p: U) => any : never) extends (i: infer I) => any ? I : never
type IntersectUnionFn<U> =  U2I<ToUFn<U>> //  (() => 'a') & (() => 'b')
type LastOfUnion<U> = IntersectUnionFn<U> extends () => infer R ? R : never
type Iter<U, T extends any[] = []> = [LastOfUnion<U>] extends [never] ? T : Iter<Exclude<U, LastOfUnion<U>>, [...T, LastOfUnion<U>]>
type UnionToTuple<U> = [LastOfUnion<U>] extends [never] ? [] : [LastOfUnion<U>, ...UnionToTuple<Exclude<U, LastOfUnion<U>>>]
type Result23_OK = Iter<'a' | 'b' | 'c'>
type Result23_OK2 = UnionToTuple<'a' | 'b' | 'c'>

type MinusOne<T extends number, C extends 1[] = [], N extends 1[]  = [1]> = T extends N['length'] ? C['length'] : MinusOne<T, [...C, 1], [...N, 1]>
type MinusN<T extends number, N extends number> = MinusOne<N> extends 0 ? MinusOne<T> : MinusN<MinusOne<T>, MinusOne<N>>
type R23a = MinusN<10, 3>

type A23 = ['c', 'b', 'a']
type R23A = MinusOne<A23['length']>

// type Reverse<T extends any[]> = {
//   [(I in (keyof T as number[])) as MinusOne<I>]: T[I]
// }

// type R23b = Reverse<A23>


// type Result22_2 = ToFn<Union22>
// type UnionToTUple<T, R extends Array<any> = []> = T extends T ? Union[...R, T] : R
// type Result22 = UnionToTUple<Union22>

// type perm = Permutation<'A' | 'B' | 'C'>; // ['A', 'B', 'C'] | ['A', 'C', 'B'] | ['B', 'A', 'C'] | ['B', 'C', 'A'] | ['C', 'A', 'B'] | ['C', 'B', 'A']

